﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Server.Search.Administration;
using Microsoft.SharePoint;
using TSB.FarmPowerShellWizard.Entities;

namespace TSB.FarmPowerShellWizard.Repository
{
    public class SearchAppRepository
    {
        private string _searchAppName;

        public string GetContentSourcesScript()
        {
            var cs = GetContentSources();

            var ps = new StringBuilder();
            ps.AppendLine(Helpers.GetComment("SEARCH_CSHeading"));
            ps.AppendFormat(Helpers.GetScriptComment("SEARCH_GetSearchApp"), _searchAppName);
            ps.AppendLine(Helpers.GetScriptComment("SEARCH_AdminContent"));

            foreach(var c in cs)
            {
                ps.AppendFormat(Helpers.GetScriptComment("SEARCH_NewContentSource"), c.Name, c.Type, c.CrawlPriority == CrawlPriorityTypes.Normal ? 1 : 2);
                
                var startAddresses = c.StartAddresses.Aggregate(string.Empty, (current, a) => current + string.Format("{1}{0}", a, current.Length > 0 ? "," : string.Empty));
                ps.Append(startAddresses);

                if (c.HasCrawlSet)
                {
                    ps.AppendLine(Helpers.GetScriptComment("SEARCH_GetContentSources"));
                }

                if (c.FullCrawl != null)
                {
                    ps.AppendFormat("\n# {0}", c.FullCrawl.Description);
                    switch(c.FullCrawl.CrawlScheduleType)
                    {
                        case CrawlScheduleTypes.Daily:
                            ps.AppendFormat("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Full -DailyCrawlSchedule -CrawlScheduleRepeatDuration {0} -CrawlScheduleRepeatInterval {1} -CrawlScheduleRunEveryInterval {2} -CrawlScheduleStartDateTime {3}", c.FullCrawl.RepeatDuration, c.FullCrawl.RepeatInterval, c.FullCrawl.RunEveryInterval, c.FullCrawl.CrawlStartTime.ToString("hh:mm") );
                            break;
                        case CrawlScheduleTypes.Weekly:
                            ps.AppendFormat("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Full -WeeklyCrawlSchedule  -CrawlScheduleDaysOfWeek {0} -CrawlScheduleRepeatDuration {1} -CrawlScheduleRepeatInterval {2} -CrawlScheduleRunEveryInterval {3} -CrawlScheduleStartDateTime {4}", c.FullCrawl.DaysOfWeekTotal, c.FullCrawl.RepeatDuration, c.FullCrawl.RepeatInterval, c.FullCrawl.RunEveryInterval, c.FullCrawl.CrawlStartTime.ToString("HH:mm"));
                            break;
                        case CrawlScheduleTypes.Monthly:
                            ps.AppendLine("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Full -MonthlyCrawlSchedule");
                            break;
                    }
                }

                if (c.IncrementalCrawl != null)
                {
                    ps.AppendFormat("\n# {0}", c.IncrementalCrawl.Description);
                    switch (c.IncrementalCrawl.CrawlScheduleType)
                    {
                        case CrawlScheduleTypes.Daily:
                            ps.AppendFormat("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Incremental  -DailyCrawlSchedule -CrawlScheduleRepeatDuration {0} -CrawlScheduleRepeatInterval {1} -CrawlScheduleRunEveryInterval {2} -CrawlScheduleStartDateTime {3}", c.IncrementalCrawl.RepeatDuration, c.IncrementalCrawl.RepeatInterval, c.IncrementalCrawl.RunEveryInterval, c.IncrementalCrawl.CrawlStartTime.ToString("hh:mm"));
                            break;
                        case CrawlScheduleTypes.Weekly:
                            ps.AppendFormat("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Incremental -WeeklyCrawlSchedule  -CrawlScheduleDaysOfWeek {0} -CrawlScheduleRepeatDuration {1} -CrawlScheduleRepeatInterval {2} -CrawlScheduleRunEveryInterval {3} -CrawlScheduleStartDateTime {4}", c.IncrementalCrawl.DaysOfWeekTotal, c.IncrementalCrawl.RepeatDuration, c.IncrementalCrawl.RepeatInterval, c.IncrementalCrawl.RunEveryInterval, c.IncrementalCrawl.CrawlStartTime.ToString("HH:mm"));
                            break;
                        case CrawlScheduleTypes.Monthly:
                            ps.AppendLine("\n$cs | Set-SPEnterpriseSearchCrawlContentSource -ScheduleType Incremental -MonthlyCrawlSchedule");
                            break;
                    }
                }
            }

            return ps.ToString();
        }

        public string GetScopesScript(string searchAppName)
        {
            var scopes = GetScopes();

            var ps = new StringBuilder();

            ps.Append(Helpers.GetComment("SEARCH_ScopeHeading"));
            ps.AppendFormat("$searchAppName = \"{0}\" \n$searchApp = Get-SPEnterpriseSearchServiceApplication -Identity $searchAppName", searchAppName);


            foreach(var scope in scopes)
            {
                ps.AppendFormat(Constants.HEADING, string.Format("Scope '{0}'", scope.Name));
                ps.AppendFormat("\nNew-SPEnterpriseSearchQueryScope -Name \"{0}\" -SearchApplication $searchApp -DisplayInAdminUI {1}",scope.Name, scope.DisplayInAdminUI ? "$true" : "$false");

                if (!string.IsNullOrEmpty(scope.Description))
                {
                    ps.AppendFormat(" -Description \"{0}\"", scope.Description);
                }

                if (!string.IsNullOrEmpty(scope.AlternateResultsPage))
                {
                    ps.AppendFormat(" -AlternateResultsPage \"{0}\"", scope.AlternateResultsPage);
                }
                if (!string.IsNullOrEmpty(scope.OwningSite))
                {
                    ps.AppendFormat(" -OwningSite \"{0}\"", scope.OwningSite);
                }

                ps.AppendFormat(Constants.SUB_HEADING, string.Format("Scope Rules for '{0}'", scope.Name));
                ps.AppendFormat("\n$scope = Get-SPEnterpriseSearchQueryScope -Identity \"{0}\" -SearchApplication $searchApp", scope.Name);

                foreach(var rule in scope.Rules)
                {
                    switch (rule.RuleType)
                    {
                        case RuleTypes.PropertyQuery:
                            ps.AppendFormat("\nNew-SPEnterpriseSearchQueryScopeRule -Scope $scope -SearchApplication $searchApp -RuleType PropertyQuery -Url http://{0} -ManagedProperty \"{1}\" -PropertyValue \"{2}\" -FilterBehavior {3}", Environment.MachineName, rule.Property, rule.PropertyValue, rule.FilterBehaviour);
                            continue;
                        case RuleTypes.Url:
                            ps.AppendFormat("\nNew-SPEnterpriseSearchQueryScopeRule -Scope $scope -RuleType Url -Url http://{0} -MatchingString \"{1}\" -FilterBehavior {2} -UrlScopeRuleType {3}", Environment.MachineName, rule.Property, rule.FilterBehaviour, rule.UrlRuleType);
                            continue;
                        case RuleTypes.AllContent:
                            ps.AppendFormat("\nNew-SPEnterpriseSearchQueryScopeRule -Scope $scope -RuleType AllContent -Url http://{0}", Environment.MachineName);
                            continue;
                    }
                    
                }
            }

            return ps.ToString();
        }

        public string GetManagedPropertiesScript(string searchAppName)
        {
            var managedProperties = GetManagedProperties();

            var ps = new StringBuilder();
            ps.AppendLine(Helpers.GetComment("SEARCH_MPHeading"));
            ps.AppendFormat("$searchAppName = \"{0}\" \n$searchApp = Get-SPEnterpriseSearchServiceApplication -Identity $searchAppName", searchAppName);


            foreach (var mp in managedProperties)
            {
                ps.AppendFormat(Constants.HEADING, string.Format("Managed Property '{0}'", mp.Name));
                ps.AppendFormat("\n$mp = New-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $searchapp -Name \"{0}\" -Type {1} -Description \"{2}\"", mp.Name, (int)mp.PropertyType, mp.Description);
                
                if (mp.EnabledForScoping)
                {
                    ps.Append(" -EnabledForScoping $true");
                }


                if (mp.Mappings != null && mp.Mappings.Count > 0)
                {
                    ps.AppendFormat(Constants.SUB_HEADING, "Mappings");
                    foreach(var map in mp.Mappings)
                    {
                        ps.AppendFormat("\n$prop = Get-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $searchapp -Name \"{0}\"", map);
                        ps.AppendFormat("\nNew-SPEnterpriseSearchMetadataMapping -SearchApplication $searchapp -CrawledProperty $prop -ManagedProperty $mp");

                    }
                }
                ps.Append("\n");
            }

            return ps.ToString();
        }
        
        public List<SearchContentSource> GetContentSources()
        {
            var contentSources = new List<SearchContentSource>();

            var proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
            var appId = proxy.GetSearchServiceApplicationInfo().SearchServiceApplicationId;

            var app = SearchService.Service.SearchApplications.GetValue<SearchServiceApplication>(appId);
            var content = new Content(app);
            _searchAppName = app.Name;
            
            foreach (ContentSource csi in content.ContentSources)
            {
                var css = new SearchContentSource
                             {
                                 Name = csi.Name,
                                 Type = csi.Type.ToString(),
                                 CrawlPriority = csi.CrawlPriority == CrawlPriority.Normal ? CrawlPriorityTypes.Normal : CrawlPriorityTypes.High,
                                 StartAddresses = new List<string>()
                             };

                foreach(Uri add in csi.StartAddresses)
                {
                    css.StartAddresses.Add(add.AbsoluteUri);
                }

                if (csi.FullCrawlSchedule != null)
                {
                    var schedule = GetSchedule(csi.FullCrawlSchedule);

                    if (schedule.CrawlScheduleType != CrawlScheduleTypes.None )
                    {
                        css.FullCrawl = schedule;
                    }
                }

                if (csi.IncrementalCrawlSchedule != null)
                {
                    var schedule = GetSchedule(csi.IncrementalCrawlSchedule);

                    if (schedule.CrawlScheduleType != CrawlScheduleTypes.None)
                    {
                        css.IncrementalCrawl = schedule;
                    }
                }

                contentSources.Add(css);
            }


            return contentSources;
        }

        public List<SearchScope> GetScopes()
        {
            var searchScopes = new List<SearchScope>();

            var proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
            var appId = proxy.GetSearchServiceApplicationInfo().SearchServiceApplicationId;


            var app = SearchService.Service.SearchApplications.GetValue<SearchServiceApplication>(appId);
            var scopes = new Scopes(app);
            var s = scopes.AllScopes;

            foreach (Scope scope in s)
            {
                var newScope = new SearchScope
                                {
                                    Name = scope.Name,
                                    Description = scope.Description,
                                    DisplayInAdminUI = scope.DisplayInAdminUI,
                                    AlternateResultsPage = scope.AlternateResultsPage,
                                    OwningSite = scope.OwningSite != null ? scope.OwningSite.Url : string.Empty,
                                    Rules = new List<SearchScopeRule>()
                                };
                
                if (scope.Rules != null)
                {
                    foreach (ScopeRule rule in scope.Rules)
                    {

                        switch (rule.GetType().ToString())
                        {
                            case "Microsoft.Office.Server.Search.Administration.PropertyQueryScopeRule":
                                var propertyRule = rule as PropertyQueryScopeRule;
                                newScope.Rules.Add(new SearchScopeRule
                                {
                                    Id = propertyRule.ID,
                                    RuleType = RuleTypes.PropertyQuery,
                                    Property = propertyRule.Property.Name,
                                    PropertyValue = propertyRule.Value,
                                    FilterBehaviour = GetFilterTypes(propertyRule.FilterBehavior)
                                });
                                
                                break;

                            case "Microsoft.Office.Server.Search.Administration.UrlScopeRule":
                                var urlRule = rule as UrlScopeRule;
                                newScope.Rules.Add(new SearchScopeRule
                                {
                                    Id = urlRule.ID,
                                    RuleType = RuleTypes.Url,
                                    Property = urlRule.MatchingString,
                                    FilterBehaviour = GetFilterTypes(urlRule.FilterBehavior),
                                    UrlRuleType = GetUrlRuleTypes(urlRule.UrlRuleType)
                                });
                                break;

                            case "Microsoft.Office.Server.Search.Administration.AllContentScopeRule":
                                var allContentRule = rule as AllContentScopeRule;
                                newScope.Rules.Add(new SearchScopeRule
                                {
                                    RuleType = RuleTypes.AllContent,
                                    Id = allContentRule.ID
                                });

                                break;
                        }
                    }

                    newScope.Rules = newScope.Rules.OrderBy(x => x.Id).ToList();
                }

                searchScopes.Add(newScope);
            }

            return searchScopes;
        }

        public List<SearchManagedProperty> GetManagedProperties()
        {
            var managedProperties = new List<SearchManagedProperty>();

            var proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
            var appId = proxy.GetSearchServiceApplicationInfo().SearchServiceApplicationId;


            var app = SearchService.Service.SearchApplications.GetValue<SearchServiceApplication>(appId);
            var schema = new Schema(app); 
            var allManagedProperties = schema.AllManagedProperties; 

            foreach(ManagedProperty mp in allManagedProperties)
            {
                if (Settings.IgnoreManagedProperty(mp.Name))
                {
                    continue;
                }

                var newProperty = new SearchManagedProperty
                                {
                                    Name = mp.Name,
                                    Description = mp.Description,
                                    PropertyType =
                                        (ManagedPropertyTypes)
                                        Enum.Parse(typeof (ManagedPropertyTypes), mp.ManagedType.ToString()),
                                        Mappings = new List<string>(),
                                    EnabledForScoping = mp.EnabledForScoping

                                };

                
                var mappings = mp.GetMappings();

                foreach(Mapping m in mappings)
                {
                    newProperty.Mappings.Add(m.CrawledPropertyName);
                }
                
                managedProperties.Add(newProperty);
            }
            

            return managedProperties;
        }


        SearchCrawlSchedule GetSchedule(Schedule schedule)
        {
            switch(schedule.GetType().ToString())
            {
                case "Microsoft.Office.Server.Search.Administration.DailySchedule":
                    var sch = (DailySchedule) schedule;

                    return new SearchCrawlSchedule
                               {
                                   CrawlScheduleType = CrawlScheduleTypes.Daily,
                                   RepeatDuration = sch.RepeatDuration,
                                   RepeatInterval = sch.RepeatInterval,
                                   RunEveryInterval = sch.DaysInterval,
                                   CrawlStartTime = new DateTime(sch.BeginYear, sch.BeginMonth, sch.BeginDay, sch.StartHour, sch.StartMinute,0),
                                   Description = sch.Description
                               };
                case "Microsoft.Office.Server.Search.Administration.WeeklySchedule":
                    var schWeekly = (WeeklySchedule)schedule;
                    return new SearchCrawlSchedule
                    {
                        CrawlScheduleType = CrawlScheduleTypes.Weekly,
                        RepeatDuration = schWeekly.RepeatDuration,
                        RepeatInterval = schWeekly.RepeatInterval,
                        RunEveryInterval = schWeekly.WeeksInterval,
                        CrawlStartTime = new DateTime(schWeekly.BeginYear, schWeekly.BeginMonth, schWeekly.BeginDay, schWeekly.StartHour, schWeekly.StartMinute,0),
                        Description = schWeekly.Description,
                        DaysOfWeek = GetDaysOfWeek(schWeekly.DaysOfWeek)

                    };
                case "Microsoft.Office.Server.Search.Administration.MonthlyDateSchedule":
                    var schMonthly = (MonthlyDateSchedule)schedule;
                    return new SearchCrawlSchedule
                    {
                        CrawlScheduleType = CrawlScheduleTypes.Weekly,
                        RepeatDuration = schMonthly.RepeatDuration,
                        RepeatInterval = schMonthly.RepeatInterval,
                        CrawlStartTime = new DateTime(schMonthly.BeginYear, schMonthly.BeginMonth, schMonthly.BeginDay, schMonthly.StartHour, schMonthly.StartMinute, 0),
                        Description = schMonthly.Description,
                        MonthsOfYear = GetMonthsOfYear(schMonthly.MonthsOfYear)

                    };
            }

            return new SearchCrawlSchedule
            {
                CrawlScheduleType = CrawlScheduleTypes.None

            };
      
        }

        DaysOfWeekTypes GetDaysOfWeek(DaysOfWeek wks)
        {
            var response = DaysOfWeekTypes.NA;

            if ((wks & DaysOfWeek.Monday) == DaysOfWeek.Monday)
            {
                response |= DaysOfWeekTypes.Monday;
            }

            if ((wks & DaysOfWeek.Tuesday) == DaysOfWeek.Tuesday)
            {
                response = response | DaysOfWeekTypes.Tuesday;
            }
            if ((wks & DaysOfWeek.Wednesday) == DaysOfWeek.Wednesday)
            {
                response = response | DaysOfWeekTypes.Wednesday;
            }
            if ((wks & DaysOfWeek.Thursday) == DaysOfWeek.Thursday)
            {
                response = response | DaysOfWeekTypes.Thursday;
            }
            if ((wks & DaysOfWeek.Friday) == DaysOfWeek.Friday)
            {
                response = response | DaysOfWeekTypes.Friday;
            }
            if ((wks & DaysOfWeek.Saturday) == DaysOfWeek.Saturday)
            {
                response = response | DaysOfWeekTypes.Saturday;
            }
            if ((wks & DaysOfWeek.Sunday) == DaysOfWeek.Sunday)
            {
                response = response | DaysOfWeekTypes.Sunday;
            }

            return response;
        }

        MonthsOfYearTypes GetMonthsOfYear(MonthsOfYear mnths)
        {
            var response = MonthsOfYearTypes.NA;

            if ((mnths & MonthsOfYear.January) == MonthsOfYear.January)
            {
                response |= MonthsOfYearTypes.January;
            }
            if ((mnths & MonthsOfYear.February) == MonthsOfYear.February)
            {
                response |= MonthsOfYearTypes.February;
            }

            if ((mnths & MonthsOfYear.March) == MonthsOfYear.January)
            {
                response |= MonthsOfYearTypes.January;
            }

            if ((mnths & MonthsOfYear.April) == MonthsOfYear.April)
            {
                response |= MonthsOfYearTypes.April;
            }

            if ((mnths & MonthsOfYear.May) == MonthsOfYear.May)
            {
                response |= MonthsOfYearTypes.May;
            }

            if ((mnths & MonthsOfYear.June) == MonthsOfYear.June)
            {
                response |= MonthsOfYearTypes.June;
            }

            if ((mnths & MonthsOfYear.July) == MonthsOfYear.January)
            {
                response |= MonthsOfYearTypes.January;
            }

            if ((mnths & MonthsOfYear.August) == MonthsOfYear.August)
            {
                response |= MonthsOfYearTypes.August;
            }

            if ((mnths & MonthsOfYear.September) == MonthsOfYear.September)
            {
                response |= MonthsOfYearTypes.September;
            }

            if ((mnths & MonthsOfYear.October) == MonthsOfYear.October)
            {
                response |= MonthsOfYearTypes.October;
            }

            if ((mnths & MonthsOfYear.November) == MonthsOfYear.November)
            {
                response |= MonthsOfYearTypes.November;
            }

            if ((mnths & MonthsOfYear.December) == MonthsOfYear.December)
            {
                response |= MonthsOfYearTypes.December;
            }


            return response;
        }

        FilterTypes GetFilterTypes(ScopeRuleFilterBehavior filter)
        {
            switch(filter)
            {
                case ScopeRuleFilterBehavior.Include:
                    return FilterTypes.Include;
                case ScopeRuleFilterBehavior.Exclude:
                    return FilterTypes.Exclude;
                case ScopeRuleFilterBehavior.Require:
                    return FilterTypes.Required;
            }

            return FilterTypes.NA;
        }

        UrlRuleTypes GetUrlRuleTypes(UrlScopeRuleType urlScopeRule)
        {
            switch (urlScopeRule)
            {
                case UrlScopeRuleType.Folder:
                    return UrlRuleTypes.Folder;
                case UrlScopeRuleType.Domain:
                    return UrlRuleTypes.Domain;
                case UrlScopeRuleType.HostName:
                    return UrlRuleTypes.HostName;
            }

            return UrlRuleTypes.NA;
        }
    }

}
